package com.bytenebula.asteroid.ddl;

import com.bytenebula.ByteNebulaException;
import com.bytenebula.ResultCodes;
import com.bytenebula.entity.meta.MetaTable;
import com.bytenebula.entity.meta.MetaWarehouse;
import com.bytenebula.asteroid.service.MetaWarehouseService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

@Component
public class SqlBuilder {

    @Autowired
    private MetaWarehouseService warehouseService;
    private Configuration configuration = null;

    public SqlBuilder() {
        try {
            ClassPathResource cpr = new ClassPathResource("templates");
            String rootPath = cpr.getFile().getPath();
            this.configuration = new Configuration(Configuration.VERSION_2_3_32);
            this.configuration.setDirectoryForTemplateLoading(new File(rootPath));
        } catch (Exception e) {
            if (this.configuration!=null)
                this.configuration.clearTemplateCache();
            throw new ByteNebulaException(ResultCodes.GENERATE_SQL_FAILED);
        }
    }

    /**
     * 创建CREATE TABLE语句
     * @param table MetaTable对象
     * @return 返回Sql语句
     */
    public String generateCreateTableDll(MetaTable table) {

        // 获取Warehouse对象
        MetaWarehouse dw = warehouseService.getById(table.getDbId());
        if (dw == null)
            throw new ByteNebulaException(ResultCodes.CANNOT_FILE_WAREHOURCE, table.getDbId());

        try {
            Map<String, Object> dataModel = new HashMap<>();
            dataModel.put("dw", dw);
            dataModel.put("table", table);
            Template template = configuration.getTemplate("ddl-create-table.ftl");
            StringWriter sw = new StringWriter();
            template.process(dataModel, sw);
            return sw.toString();
        } catch (Exception e) {
            if (this.configuration!=null)
                this.configuration.clearTemplateCache();
            throw new ByteNebulaException(ResultCodes.GENERATE_SQL_FAILED, e);
        }
    }

    /**
     * 实现数据类型的映射
     * @param sourceDataType 源数据类型
     * @param precious 精度
     * @param scale 小数位数
     * @return 返回数据仓库中的数据类型，如果无法映射则报错
     */
    private String typeMapping(String sourceDataType, Integer precious, Integer scale) {
        String dt = sourceDataType.toUpperCase().trim();
        return switch (dt) {
//            case "CHAR" ->
//                // CHAR => CHAR
//                "{%s}({%d})".formatted(DataTypes.CHAR.getTypeCode(), precious);
//            case "VARCHAR" ->
//                // VARCHAR => VARCHAR
//                "{%s}({%d})".formatted(DataTypes.VARCHAR.getTypeCode(), precious);
            case "NVARCHAR", "CHAR", "VARCHAR", "TEXT", "NCHAR", "NTEXT" ->
                // NVARCHAR => STRING
                // CHAR => STRING
                // VARCHAR => STRING
                // TEXT => STRING
                // NCHAR => STRING
                // NTEXT => STRING
                DataTypes.STRING.getTypeCode();
            case "BIT" ->
                // BIT => BOOLEAN
                DataTypes.BOOLEAN.getTypeCode();
            case "TINYINT" ->
                // TINYINT => SMALLINT
                DataTypes.SMALLINT.getTypeCode();
            case "REAL" ->
                // REAL => FLOAT
                DataTypes.FLOAT.getTypeCode();
            case "FLOAT" ->
                // FLOAT => DOUBLE
                DataTypes.DOUBLE.getTypeCode();
            case "MONEY" ->
                // MONEY => DECIMAL(19,4)
                DataTypes.DECIMAL.getTypeCode() + "(19,4)";
            case "SMALLMONEY" ->
                // SMALLMONEY => DECIMAL(10,4)
                DataTypes.DECIMAL.getTypeCode() + "(10,4)";
            case "DECIMAL", "NUMERIC" ->
                // DECIMAL => DECIMAL(M,N)
                // NUMERIC => DECIMAL(M,N)
                "{%s}({%d},{%d})".formatted(DataTypes.DECIMAL.getTypeCode(), precious, scale);
            case "DATE" ->
                // DATE => DATE
                DataTypes.DATE.getTypeCode();
            case "DATETIME", "DATETIME2", "SMALLDATETIME" ->
                // DATETIME => DATETIMEV2
                // DATETIME2 => DATETIMEV2
                // SMALLDATETIME => DATETIMEV2
                DataTypes.DATE.getTypeCode();
            case "TIME", "DATETIMEOFFSET", "TIMESTAMP" ->
                // TIME => STRING
                // DATETIMEOFFSET => STRING
                // TIMESTAMP => STRING
                DataTypes.STRING.getTypeCode();
            default -> throw new ByteNebulaException(ResultCodes.INVALID_COLUMN_TYPE, sourceDataType);
        };

    }
}
